package io.hepu.robotize.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.reflect.TypeToken;

import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public final class Jacksons {
    private static final ObjectMapper mapper = new ObjectMapper();
    private final static String DATE_TIME_FORMATTER = "yyyy-MM-dd HH:mm:ss";

    static {
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));

        // 全局处理时间 ，SimpleDateFormat的构造函数参数是序列化需要
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") {
            // 该方法是反序列化需要
            @Override
            public Date parse(String source) throws ParseException {
                return JDate.parse(source);
            }
        });
    }

    // bean -> json
    // map -> json
    // list -> json
    public static <T> String serialize(T obj) {
        try {
            return mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    // json -> simple bean
    public static <T> T deserialize(String content, Class<T> clazz) {
        try {
            return mapper.readValue(content, clazz);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    // json -> complex bean
    public static <T> T deserialize(String content, TypeReference<T> typeReference) {
        try {
            return mapper.readValue(content, typeReference);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    // 新增的TypeToken反序列化方法，支持复杂泛型
    public static <T> T deserialize(String content, TypeToken<T> typeToken) {
        try {
            JavaType javaType = getJavaTypeFromTypeToken(typeToken);
            return mapper.readValue(content, javaType);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    // 获取JavaType以处理复杂泛型
    private static JavaType getJavaTypeFromTypeToken(TypeToken<?> typeToken) {
        return mapper.getTypeFactory().constructType(typeToken.getType());
    }

    // json -> bean list
    public static <T> List<T> deserialize(String content, Type type) {
        try {
            TypeReference<List<T>> typeReference = new TypeReference<List<T>>() {
                @Override
                public Type getType() {
                    return type;
                }
            };
            return mapper.readValue(content, typeReference);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    // Conversion from given value, into instance of given value type,
    // by writing value into temporary buffer and reading from the buffer into specified target type.
    public static <T> T convert(Object fromValue, Class<T> clazz) {
        return mapper.convertValue(fromValue, clazz);
    }

    // Handles simple generic types
    public static <T> T convert(Object fromValue, TypeReference<T> typeRef) {
        return mapper.convertValue(fromValue, typeRef);
    }

    public static Map<String, Object> map(Object fromValue) {
        return convert(fromValue, new TypeReference<Map<String, Object>>() {});
    }

    // Binds data given JSON tree contains into specific value (usually bean) type.
    public static <T> T convert(TreeNode tree, Class<T> clazz) throws JsonProcessingException {
        return mapper.treeToValue(tree, clazz);
    }

    // Handles complex generic types
    public static Map<String, Object> mapping(Object fromValue) {
        JavaType javaType = mapper.getTypeFactory().constructParametricType(Map.class, String.class, Object.class);
        return mapper.convertValue(fromValue, javaType);
    }

    // json -> bean list
    public static <T> List<T> list(String content, Class<T> clazz) {
        return deserialize(content, getCollectionType(List.class, clazz));
    }

    public static JavaType getCollectionType(Class<?> parametrized, Class<?>... elementClasses) {
        return mapper.getTypeFactory().constructParametricType(parametrized, elementClasses);
    }
}
